﻿using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml.Serialization;

namespace VisualMusic
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public Setting setting = new Setting();
        private WasapiLoopbackCapture capture = new WasapiLoopbackCapture();
        private float[] audioValues = new float[0];
        private Rectangle[] rectangles = new Rectangle[0];
        public SettingWindow? settingWindow;
        public MainWindow()
        {
            InitializeComponent();
            settingMenuItem.MouseLeftButtonDown += (s, e) =>
            {
                if (settingWindow == null)
                {
                    settingWindow = new SettingWindow(this, setting);
                }
                settingWindow.Show();
            };
            //aboutMenuItem.MouseLeftButtonDown += (s, e) =>
            //{
            //    if (settingWindow == null)
            //    {
            //        settingWindow = new SettingWindow(this, setting, 2);
            //    }
            //    settingWindow.Show();
            //};
            refreshMenuItem.MouseLeftButtonDown += (s, e) => { InitCapture(); };
            exitMenuItem.MouseLeftButtonDown += (s, e) => { Close(); };
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.setting.OnReload = LoadRectangles;
            LoadRectangles();
            CompositionTarget.Rendering += (s, e) =>
            {
                var count = Math.Min(audioValues.Length, rectangles.Length);
                var waveWidth = canvas.ActualWidth * setting.Width;
                var waveHeight = canvas.ActualHeight * setting.Height;
                var centerX = canvas.ActualWidth * setting.CenterX;
                var centerY = canvas.ActualHeight * setting.CenterY;
                var waveLeft = centerX - waveWidth / 2;
                var waveTop = centerY - waveHeight / 2;
                var waveRight = centerX + waveWidth / 2;
                var waveBottom = centerY + waveHeight / 2;
                var lineWidth = waveWidth / count * setting.LineWidth;
                var lineHeight = waveHeight / count * setting.LineWidth;
                for (int i = 0; i < count; i++)
                {
                    Rectangle rectangle = rectangles[i];
                    if (setting.Corners)
                    {
                        if (setting.Direction == VisualDirection.Up || setting.Direction == VisualDirection.Down)
                        {
                            rectangle.RadiusX = rectangle.Width / 2;
                            rectangle.RadiusY = rectangle.Width / 2;
                        }
                        else
                        {
                            rectangle.RadiusX = rectangle.Height / 2;
                            rectangle.RadiusY = rectangle.Height / 2;
                        }
                    }
                    else
                    {
                        rectangle.RadiusX = 0;
                        rectangle.RadiusY = 0;
                    }
                    //var audioVal = audioValues[(int)(i * audioValues.Length / count * (setting.SamplingEnd - setting.SamplingStart) / 1000f + audioValues.Length * setting.SamplingStart / 1000f)];
                    var audioVal = audioValues[i];
                    var strength = Math.Abs(audioVal * setting.Gain);
                    switch (setting.Direction)
                    {
                        case VisualDirection.Up:
                            rectangle.Width = lineWidth;
                            rectangle.Height = strength > rectangle.Height ? strength : (rectangle.Height - setting.AttenuationThreshold > 0 ? rectangle.Height * setting.Attenuation : 0);
                            Canvas.SetLeft(rectangle, waveLeft + i * waveWidth / count);
                            if (setting.IsTowWay && audioVal < 0)
                            {
                                Canvas.SetTop(rectangle, centerY);
                            }
                            else
                            {
                                Canvas.SetTop(rectangle, centerY - rectangle.Height);
                            }
                            break;
                        case VisualDirection.Down:
                            rectangle.Width = lineWidth;
                            rectangle.Height = strength > rectangle.Height ? strength : (rectangle.Height - setting.AttenuationThreshold > 0 ? rectangle.Height * setting.Attenuation : 0);
                            Canvas.SetLeft(rectangle, waveLeft + i * waveWidth / count);
                            if (setting.IsTowWay && audioVal < 0)
                            {
                                Canvas.SetTop(rectangle, centerY - rectangle.Height);
                            }
                            else
                            {
                                Canvas.SetTop(rectangle, centerY);
                            }
                            break;
                        case VisualDirection.Left:
                            rectangle.Height = lineHeight;
                            rectangle.Width = strength > rectangle.Width ? strength : (rectangle.Width - setting.AttenuationThreshold > 0 ? rectangle.Width * setting.Attenuation : 0);
                            Canvas.SetTop(rectangle, waveTop + i * waveHeight / count);
                            if (setting.IsTowWay && audioVal < 0)
                            {
                                Canvas.SetLeft(rectangle, centerX);
                            }
                            else
                            {
                                Canvas.SetLeft(rectangle, centerX - rectangle.Width);
                            }
                            break;
                        case VisualDirection.Right:
                            rectangle.Height = lineHeight;
                            rectangle.Width = strength > rectangle.Width ? strength : (rectangle.Width - setting.AttenuationThreshold > 0 ? rectangle.Width * setting.Attenuation : 0);
                            Canvas.SetTop(rectangle, waveTop + i * waveHeight / count);
                            if (setting.IsTowWay && audioVal < 0)
                            {
                                Canvas.SetLeft(rectangle, centerX - rectangle.Width);
                            }
                            else
                            {
                                Canvas.SetLeft(rectangle, centerX);
                            }
                            break;
                        default:
                            break;
                    }
                }
            };
            InitCapture();
        }

        private void InitCapture()
        {
            capture.StopRecording();
            capture.Dispose();
            capture = new WasapiLoopbackCapture();
            capture.DataAvailable += (s, args) =>
            {
                var buffer = new WaveBuffer(args.Buffer);
                Dispatcher.BeginInvoke((float[] vs) => { audioValues = vs; }, buffer.FloatBuffer.Take(args.BytesRecorded / 4).ToArray());
            };
            capture.RecordingStopped += (s, a) =>
            {
                capture.Dispose();
            };
            capture.StartRecording();
        }

        private void LoadRectangles()
        {
            canvas.Children.Clear();
            Rectangle[] rs = new Rectangle[setting.LineCount];
            for (int i = 0; i < setting.LineCount; i++)
            {
                Rectangle rectangle = new Rectangle();
                if (setting.IsGradient)
                {
                    var color = ColorHelper.ColorConverter.HsvToRgb(new ColorHelper.HSV(i * (setting.HueEnd - setting.HueStart) / setting.LineCount + setting.HueStart, setting.Saturation, setting.Brightness));
                    rectangle.Fill = new SolidColorBrush(Color.FromArgb((byte)setting.ColorA, color.R, color.G, color.B));
                }
                else
                {
                    rectangle.Fill = new SolidColorBrush(Color.FromArgb((byte)setting.ColorA, (byte)setting.ColorR, (byte)setting.ColorG, (byte)setting.ColorB));
                }
                rectangle.Width = 10;
                rectangle.Height = 10;
                Canvas.SetTop(rectangle, 0);
                Canvas.SetLeft(rectangle, 0);
                rs[i] = rectangle;
                canvas.Children.Add(rectangle);
            }
            rectangles = rs;
        }

        private void Window_Closing(object sender, CancelEventArgs e)
        {
            capture.StopRecording();
            Environment.Exit(0);
        }
    }
}
